﻿#if UNITY_EDITOR
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
using Object = UnityEngine.Object;

public static class ScriptGenerateHelper
{
    static StringBuilder sb = new StringBuilder(1024);
    static StringBuilder tabLevel = new StringBuilder(1024);
    static int level = 0;
    static int Level
    {
        get
        {
            return level;
        }
        set
        {
            if(value != level)
            {
                if(value < level)
                {
                    int count = level - value;
                    tabLevel.Remove(tabLevel.Length - count, count);
                }
                else
                {
                    for (int i = level; i < value; i++)
                    {
                        tabLevel.Append("\t");
                    }
                }
            }
            level = value;
        }
    }

    public static void Clear()
    {
        sb.Clear();
        tabLevel.Clear();
    }

    public static void Append(string content)
    {
        sb.Append(tabLevel);
        sb.Append(content);
    }

    public static void AppendLine(string content = "")
    {
        sb.Append(tabLevel);
        sb.AppendLine(content);
    }

    public static void PushLevel()
    {
        AppendLine("{");
        Level += 1;
    }

    public static void PopLevel()
    {
        Level -= 1;
        AppendLine("}");
    }

    public static void Save(string path)
    {
        var dir = System.IO.Path.GetDirectoryName(path);
        if(!System.IO.Directory.Exists(dir))
        {
            System.IO.Directory.CreateDirectory(dir);
        }
        System.IO.File.WriteAllText(path, sb.ToString());
    }
}

[CustomEditor(typeof(ScriptBinder))]
public class ScriptBinderEditor : Editor
{
    ///这些组件不会自动加入ScriptBinder
    private static readonly IgnoreType[] ignoreType = new[]
    {
        new IgnoreType(typeof(CanvasRenderer)),
        new IgnoreType(typeof(ScriptBinder)),
        new IgnoreType(typeof(Collider), true),
        new IgnoreType(typeof(Rigidbody)),
    };


    ///名字不满足变量条件的物体不会自动加入ScriptBinder
    private static readonly string regVarName = @"[^\w\d\$_]";

    //名字结尾是key或数组的内容时类型为key
    private static readonly Dictionary<Type, string[]> classNameDic = new Dictionary<Type, string[]>()
    {
        {typeof(GameObject), new[] {"GameObject", "Go", "go"} },
        {typeof(ScrollRect), new []{"ScrollRect", "Scroll", "scroll"}},
        {typeof(Text), new []{"Text", "Lb", "lb"}},
        {typeof(InputField), new []{"InputField", "Input", "input"}},
        {typeof(Image), new []{"Image", "Img", "img"}},
        {typeof(Toggle), new []{"Toggle", "Tog", "tog"}},
        {typeof(RawImage), new []{"RawImage", "Raw", "raw"}},
        {typeof(Button), new []{"Button", "Btn", "btn"}},
        //{typeof(TextMeshProUGUI), new []{"TextMeshProUGUI", "TextMesh", "textMesh"}},
    };

    private static bool NeedcheckClassNameDic = true;

    protected SerializedProperty components = null;

    private GUIStyle hintStyle;

    private string initHint = string.Empty;

    StringBuilder stringBuilder = new StringBuilder(1024);
    ScriptBinder scriptBinder;

    public void OnEnable()
    {
        components = serializedObject.FindProperty("scriptItems");
        hintStyle = new GUIStyle(){richText = true, wordWrap = true, fontSize = 14};
        hintStyle.normal.textColor = Color.white;
        hintStyle.stretchHeight = true;
        scriptBinder = this.target as ScriptBinder;

        CheckClassNameDic();
    }

    private void CheckClassNameDic()
    {
        if (!NeedcheckClassNameDic) return;
        NeedcheckClassNameDic = false;
        var dic = new Dictionary<string, List<string>>();
        foreach (var kv in classNameDic)
        {
            for (var i = 0; i < kv.Value.Length; i++)
            {
                var shortName = kv.Value[i];
                if (!dic.TryGetValue(shortName, out var conflictArr))
                {
                    conflictArr = new List<string>() {kv.Key.Name};
                    dic.Add(shortName, conflictArr);
                }
                else
                {
                    conflictArr.Add(kv.Value[i]);
                }
            }
        }

        var str = string.Empty;
        foreach (var kv in dic)
        {
            if (kv.Value.Count > 1)
            {
                str += " {shortname:" + kv.Key + " class:";
                for (var i = 0; i < kv.Value.Count; i++)
                {
                    str += kv.Value[i] + " ";
                }
                str += "}";
            }
        }
            
        if(!string.IsNullOrEmpty(str)) Debug.LogError("ScriptBinderEditor.classNameDic冲突: " + str);
    }

    public override void OnInspectorGUI()
    {
        serializedObject.Update(); //从对象抓取的最新数据

        GUI.color = new Color32(0x6a, 0xff, 0x5e, 0xFF);

        GUILayout.BeginVertical("GroupBox");

        GUI.color = Color.gray;
        GUILayout.BeginVertical("GroupBox");
        GUI.color = Color.magenta;
        EditorGUILayout.LabelField("ClientScriptBinder(神器)", GUILayout.MinWidth(100));
        GUILayout.EndVertical();

        GUI.color = Color.gray;
        GUILayout.BeginVertical("GroupBox");
        GUI.color = Color.white;
        base.OnInspectorGUI();
        GUILayout.EndVertical();
        OnScriptItemGUI();
        OnDragAddItemGUI();
        OnGenerateScriptGUI();
        GUILayout.EndVertical();

        serializedObject.ApplyModifiedProperties();
        serializedObject.UpdateIfRequiredOrScript();
    }

    protected void OnScriptItemGUI()
    {
        GUILayout.BeginVertical("GroupBox");

        if (components.arraySize == 0)
        {
            GUI.color = Color.yellow;
            if (GUILayout.Button("初始化"))
            {
                //为自己和自己的子物体中附加了ScriptBinder但是没有产生Script项的物体生成脚本
                AutoInitScriptBinder(target as ScriptBinder);
            }

            var header = "<color=#00ff00>■</color>";//<color=#ffff00></color>
            if (string.IsNullOrEmpty(initHint))
            {
                
                //不要对会动态生成销毁的物体生成
                initHint = header + "初始化自动为物体和子物体处理ScriptBinder\n" +
                           //多个完全一样的物体会使用同一个名字, 这个比较难受, 但是变量名会自动加后缀区别不影响使用
                           //要解决这个问题可以在sb脚本里指定名字, 或者自己Transfrom.Find
                           header + "加入物体的名字必须符合<color=#ffff00>变量名规范</color>并且以<color=#ffff00>下划线_</color>分隔\n" +
                           header + "<color=#ffff00>ScriptBinderEditor.ignoreType</color>中定义的组件不会自动加入\n";
                initHint += header + "使用特殊结尾<color=#ffff00>ScriptBinderEditor.classNameDic</color>指定物体的加入类型:\n";
                var header2 = "    <color=#00ff00>□</color>";
                foreach (var kv in classNameDic)
                {
                    initHint += header2 + "<color=#ffff00>" + kv.Key.Name + "</color> : ";
                    initHint += kv.Key.Name;
                    for (var i = 0; i < kv.Value.Length; i++)
                    {
                        initHint += ", " + kv.Value[i];
                    }
                    initHint += "\n";
                }

                initHint += "<color=#888888>备注 : \n■这个脚本是挂载在预制体的根节点上.\n" +
                            "■尽量使用特殊结尾,这样改动之后再生成的文件更一致.\n" +
                            "■变量名会忽略GameObject.name的下划线前缀还会在后面加上后缀,他们几乎不相等,使用时小心.</color>";
            }
            
            GUILayout.Label(initHint, hintStyle);
        }
        else
        {
            GUI.color = Color.yellow;
            if (GUILayout.Button("清空"))
            {
                ClearScriptBinderInChildren(target as ScriptBinder);
            }
            
            for (int i = 0; i < components.arraySize; i++)
            {
                var scriptBindItem = components.GetArrayElementAtIndex(i);
                if (scriptBindItem != null)
                {
                    GUI.color = Color.gray;
                    SerializedProperty component = scriptBindItem.FindPropertyRelative("component");
                    SerializedProperty varName = scriptBindItem.FindPropertyRelative("varName");
                    ScriptBinderItem scriptItem = null;
                    if (i < (target as ScriptBinder).scriptItems.Length)
                    {
                        scriptItem = (target as ScriptBinder).scriptItems[i];
                    }

                    EditorGUILayout.Space(); //    在上一个控件和跟随的控件之间，制作一个小的空距
                    EditorGUILayout.BeginHorizontal(); //开始水平组
                    GUI.color = Color.white; //绘制颜色为白色
                    if (string.IsNullOrEmpty(varName.stringValue) && component != null && component.objectReferenceValue != null)
                    {
                        varName.stringValue = component.objectReferenceValue.name;
                    }

                    varName.stringValue = EditorGUILayout.TextField(varName.stringValue);
                    GUI.color = Color.white;
                    component.objectReferenceValue = EditorGUILayout.ObjectField(component.objectReferenceValue, typeof(UnityEngine.Object), true) as UnityEngine.Object;

                    if (component.objectReferenceValue != null)
                    {
                        GameObject gameObject = component.objectReferenceValue as GameObject;
                        if (gameObject == null)
                        {
                            if (component.objectReferenceValue is UnityEngine.Component unityComponent)
                            {
                                gameObject = unityComponent.gameObject;
                            }
                        }

                        if (GUILayout.Button("S", "GV Gizmo DropDown", GUILayout.MaxWidth(30)))
                        {
                            UnityEngine.Component[] coms = gameObject.GetComponents<UnityEngine.Component>();
                            GenericMenu menu = new GenericMenu(); //右键菜单
                            menu.AddItem(new GUIContent("GameObject"), component.objectReferenceValue is GameObject, _menuFunction, new object[] {gameObject, scriptItem});
                            if (coms != null)
                            {
                                for (int j = 0; j < coms.Length; j++)
                                {
                                    menu.AddItem(new GUIContent(coms[j].GetType().Name), component.objectReferenceValue == coms[j], _menuFunction, new object[] {coms[j], scriptItem});
                                }
                            }

                            menu.ShowAsContext();
                        }
                    }

                    if (GUILayout.Button("X"))
                    {
                        components.DeleteArrayElementAtIndex(i);
                    }

                    EditorGUILayout.EndHorizontal();
                }
            }
        }

        GUILayout.EndVertical(); //结束垂直组
        GUI.color = Color.gray;
        EditorGUILayout.BeginVertical("GroupBox");
        GUI.color = Color.white;
        EditorGUILayout.BeginHorizontal();
        GUI.color = Color.green;
        EditorGUILayout.LabelField("Object TotalCount", GUILayout.MinWidth(60));
        GUI.color = Color.white;
        GUI.enabled = false;
        EditorGUILayout.IntField(components.arraySize, GUILayout.MinWidth(60));
        GUI.enabled = true;
        if (GUILayout.Button(" +1 ", GUILayout.MinWidth(40)))
        {
            components.InsertArrayElementAtIndex(components.arraySize); //在数组的指定索引处插入一个空元素。
            var scriptBindItem = components.GetArrayElementAtIndex(components.arraySize - 1);
            SerializedProperty component = scriptBindItem.FindPropertyRelative("component");
            SerializedProperty varName = scriptBindItem.FindPropertyRelative("varName");
            if (component.objectReferenceValue != null) component.objectReferenceValue = null;
            if (!string.IsNullOrEmpty(varName.stringValue)) varName.stringValue = "";
        }

        if (GUILayout.Button(" +10 ", GUILayout.MinWidth(40)))
        {
            for (int i = 0; i < 10; i++)
            {
                components.InsertArrayElementAtIndex(components.arraySize); //在数组的指定索引处插入一个空元素。
                var scriptBindItem = components.GetArrayElementAtIndex(components.arraySize - 1);
                SerializedProperty component = scriptBindItem.FindPropertyRelative("component");
                SerializedProperty varName = scriptBindItem.FindPropertyRelative("varName");
                if (component.objectReferenceValue != null) component.objectReferenceValue = null;
                if (!string.IsNullOrEmpty(varName.stringValue)) varName.stringValue = "";
            }
        }

        EditorGUILayout.EndHorizontal();
        EditorGUILayout.EndVertical();
    }

    void _menuFunction(object value)
    {
        var argv = value as object[];
        if (argv != null && argv.Length == 2)
        {
            try
            {
                ScriptBinderItem component = argv[1] as ScriptBinderItem;
                if (component != null)
                {
                    component.component = argv[0] as UnityEngine.Object;
                }
            }
            catch (System.Exception ex)
            {
                UnityEngine.Debug.LogErrorFormat(ex.ToString());
            }
        }
    }

    private void OnDragAddItemGUI()
    {
        var eventType = Event.current.type;
        if (eventType == EventType.DragUpdated || eventType == EventType.DragPerform)
        {
            DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
            if (eventType == EventType.DragPerform)
            {
                DragAndDrop.AcceptDrag();
                foreach (var o in DragAndDrop.objectReferences)
                {
                    AddReference(o.name, o);
                }
            }

            Event.current.Use();
        }
    }

    void GenerateMain()
    {
        ScriptGenerateHelper.Clear();
        ScriptGenerateHelper.AppendLine("namespace Framework.UI");
        ScriptGenerateHelper.PushLevel();

        ScriptGenerateHelper.AppendLine($"public partial class {scriptBinder.name} : UIFrame");
        ScriptGenerateHelper.PushLevel();

        //declare event func
        for (int i = 0; i < scriptBinder.scriptItems.Length; ++i)
        {
            var item = scriptBinder.scriptItems[i];
            if (item.component is UnityEngine.UI.Button)
            {
                ScriptGenerateHelper.AppendLine($"void On{item.varName}Clicked()");
                ScriptGenerateHelper.PushLevel();
                ScriptGenerateHelper.AppendLine($"//Add Your Code Here");
                ScriptGenerateHelper.PopLevel();
            }
            else if (item.component is TMPro.TMP_InputField)
            {
                ScriptGenerateHelper.AppendLine($"void On{item.varName}EndEdit(string {item.varName.Substring(0,1).ToLower() + item.varName.Substring(1)})");
                ScriptGenerateHelper.PushLevel();
                ScriptGenerateHelper.AppendLine($"//Add Your Code Here");
                ScriptGenerateHelper.PopLevel();
            }
            else if(item.component is UnityEngine.UI.Toggle)
            {
                ScriptGenerateHelper.AppendLine($"void On{item.varName}ValueChanged(bool isOn)");
                ScriptGenerateHelper.PushLevel();
                ScriptGenerateHelper.AppendLine($"//Add Your Code Here");
                ScriptGenerateHelper.PopLevel();
            }
        }

        ScriptGenerateHelper.PopLevel();
        //UIFrame
        ScriptGenerateHelper.PopLevel();
        ScriptGenerateHelper.Save($"Assets/Scripts/Hot/UI/UILogic/{scriptBinder.name}.cs");
    }

    void GenerateVarDeclares()
    {
        ScriptGenerateHelper.Clear();
        ScriptGenerateHelper.AppendLine("namespace Framework.UI");
        ScriptGenerateHelper.PushLevel();

        ScriptGenerateHelper.AppendLine($"public partial class {scriptBinder.name} : UIFrame");
        ScriptGenerateHelper.PushLevel();

        //generate var
        for (int i = 0; i < scriptBinder.scriptItems.Length; ++i)
        {
            var item = scriptBinder.scriptItems[i];
            ScriptGenerateHelper.AppendLine($"{item.component.GetType().FullName} {item.varName};");
        }

        ScriptGenerateHelper.AppendLine($"protected override void InitScriptBinder()");
        ScriptGenerateHelper.PushLevel();
        for(int i = 0;i<scriptBinder.scriptItems.Length;++i)
        {
            var item = scriptBinder.scriptItems[i];
            ScriptGenerateHelper.AppendLine($"{item.varName} = scriptBinder.GetValue() as {item.component.GetType().FullName};");
        }
        ScriptGenerateHelper.PopLevel();
        ScriptGenerateHelper.AppendLine();

        ScriptGenerateHelper.AppendLine($"protected override void RegisterUIEvent()");
        ScriptGenerateHelper.PushLevel();
        //register event
        for (int i = 0; i < scriptBinder.scriptItems.Length; ++i)
        {
            var item = scriptBinder.scriptItems[i];
            if (item.component is UnityEngine.UI.Button)
            {
                ScriptGenerateHelper.AppendLine($"{item.varName}.onClick.AddListener(this.On{item.varName}Clicked);");
            }
            else if (item.component is TMPro.TMP_InputField)
            {
                ScriptGenerateHelper.AppendLine($"{item.varName}.onEndEdit.AddListener(this.On{item.varName}EndEdit);");
            }
            else if(item.component is UnityEngine.UI.Toggle tg)
            {
                ScriptGenerateHelper.AppendLine($"{item.varName}.onValueChanged.AddListener(this.On{item.varName}ValueChanged);");
            }
        }
        ScriptGenerateHelper.PopLevel();
        ScriptGenerateHelper.AppendLine();

        ScriptGenerateHelper.AppendLine($"protected override void UnregisterUIEvent()");
        ScriptGenerateHelper.PushLevel();
        //remove event
        for (int i = 0; i < scriptBinder.scriptItems.Length; ++i)
        {
            var item = scriptBinder.scriptItems[i];
            if (item.component is UnityEngine.UI.Button)
            {
                ScriptGenerateHelper.AppendLine($"{item.varName}.onClick.RemoveListener(this.On{item.varName}Clicked);");
            }
            else if (item.component is TMPro.TMP_InputField)
            {
                ScriptGenerateHelper.AppendLine($"{item.varName}.onEndEdit.RemoveListener(this.On{item.varName}EndEdit);");
            }
            else if (item.component is UnityEngine.UI.Toggle tg)
            {
                ScriptGenerateHelper.AppendLine($"{item.varName}.onValueChanged.RemoveListener(this.On{item.varName}ValueChanged);");
            }
        }
        ScriptGenerateHelper.PopLevel();


        ScriptGenerateHelper.PopLevel();

        ScriptGenerateHelper.PopLevel();
        ScriptGenerateHelper.Save($"Assets/Scripts/Hot/UI/UIGenerated/{scriptBinder.name}.cs");
    }

    void OnGenerateScriptGUI()
    {
        if(null != scriptBinder && GUILayout.Button("生成客户端脚本"))
        {
            GenerateVarDeclares();
            GenerateMain();
            AssetDatabase.Refresh();
        }
    }

    ///初始化ScriptBinder, 如果子物体有ScriptBinder也会初始化它
    private void AutoInitScriptBinder(ScriptBinder sb)
    {
        var nameConflictDic = new Dictionary<string, int>();
        var so = new SerializedObject(sb);
        AddToSBObject(sb, sb.gameObject, nameConflictDic, so);
    }

    ///把此物体自己的和子物体的组件加入ScriptBinder, 如果没有可以加入的则把自己的Transform加入进去
    ///如果某个子物体有ScriptBinder, 则把子物体的GameObject加入ScriptBinder, 然后独立执行AddSBObjectItem
    ///默认他自己已经进行过ScriptBinder判断了
    private bool AddToSBObject(ScriptBinder sb, GameObject go, Dictionary<string, int> nameConflictDic, SerializedObject so)
    {
        if (go == null) return false;
        if (GameObjectNameSkipped(go.name)) return false;
        var hasComp = false;
        
        //自己的组件
        if(!Regex.IsMatch(go.name, regVarName)) hasComp = AddToSBObjectSelf(sb, go, nameConflictDic, so);
        
        //子物体的组件
        for (int i = 0, max = go.transform.childCount; i < max; i++)
        {
            var child = go.transform.GetChild(i);
            if (GameObjectNameSkipped(child.name)) continue;
            var childSB = child.GetComponent<ScriptBinder>();
            if (childSB == null)
            {
                if (AddToSBObject(sb, child.gameObject, nameConflictDic, so)) hasComp = true;
            }
            else
            {
                //这里要注意一下, 应该很少遇到.
                //如果多个子物体名字一样并且也挂了scriptbinder, 因为名字一样后面生成的物体创建的类会[覆盖]前面的
                /////如果这些相同物体是预制
                //////1.预制的ScriptBinderItem不为空, 不会为它生成类文件, 这种不受影响
                //////2.预制的ScriptBinderItem为空, 它会为预制的实例序列化, 如果以后预制单独编辑增加了ScriptBinderItem, 不知道这两者会怎么影响
                if(GameObjectNameIncluded(child.gameObject.name) && AddToSBComponent(sb, child.gameObject, nameConflictDic, so, false)) hasComp = true;
                if (childSB.scriptItems.Length == 0) AutoInitScriptBinder(childSB);
            }
        }
        return hasComp;
    }

    ///把物体自己的组件加入ScriptBinder
    private bool AddToSBObjectSelf(ScriptBinder sb, GameObject go, Dictionary<string, int> nameConflictDic, SerializedObject so)
    {
        if (go == null) return false;
        if (!GameObjectNameIncluded(go.name)) return false;

        Object[] compArr = null;
        var type = GetSpecifiedClass(go.name);
        if (type == null) compArr = go.GetComponents<Component>();
        else if(type == typeof(GameObject)) compArr = new[] { go };
        else compArr = go.GetComponents(type);

        var classSpecified = type != null;
        var hasComp = false;
        for (var i = 0; i < compArr.Length; i++)
        {
            var c = compArr[i];
            if(AddToSBComponent(sb, c, nameConflictDic, so, classSpecified)) hasComp = true;
        }
        
        if(!hasComp) AddToSBComponent(sb, go, nameConflictDic, so);

        return hasComp;
    }

    private void AddReference(string key, Object obj)
    {
        int index = components.arraySize;
        components.InsertArrayElementAtIndex(index);
        var element = components.GetArrayElementAtIndex(index);
        var gameObj = obj as GameObject;
        Object component = null;
        if (gameObj != null)
        {
            if (component == null && gameObj.GetComponent<BoxCollider>()) component = obj;
            if (component == null) component = gameObj.GetComponent<Text>();
            if (component == null) component = gameObj.GetComponent<Image>();
            if (component == null) component = gameObj.GetComponent<RawImage>();
            if (component == null) component = gameObj.GetComponent<ScrollRect>();
        }
        if (component == null) component = obj;
        element.FindPropertyRelative("component").objectReferenceValue = component;
        RemoveGameObjectNameIncluded(ref key);
        element.FindPropertyRelative("varName").stringValue = key;
    }
    
    ///把某个物体的GameObject或者组件加入ScriptBinder
    ///不判断GameObjectNameIncluded
    private bool AddToSBComponent(ScriptBinder sb, Object c, Dictionary<string, int> nameConflictDic, SerializedObject so, bool classSpecified = false)
    {
        if (c == null) return false;
        var t = c.GetType();
        if (!classSpecified && ComponentTypeExcluded(t)) return false;

        string varName;
        if (c is GameObject targetGo)
        {
            varName = targetGo.name;
        }
        else if (c is Component targetComponent)
        {
            varName =  classSpecified ? targetComponent.gameObject.name : targetComponent.gameObject.name + t.Name; 
        }
        else return false;

        RemoveGameObjectNameIncluded(ref varName);
        if (nameConflictDic.TryGetValue(varName, out var conflictCount))
        {
            var orginName = varName;
            conflictCount++;
            varName += "_" + conflictCount;
            nameConflictDic[orginName] = conflictCount;    
        }
        else
        {
            nameConflictDic.Add(varName, 0);
        }
        
        var soscriptItems = so.FindProperty("scriptItems");
        soscriptItems.InsertArrayElementAtIndex(soscriptItems.arraySize);
        var scriptBindItem = soscriptItems.GetArrayElementAtIndex(soscriptItems.arraySize - 1);
        var itemComponent = scriptBindItem.FindPropertyRelative("component");
        var itemVarName = scriptBindItem.FindPropertyRelative("varName");
        itemComponent.objectReferenceValue = c;
        itemVarName.stringValue = varName;
        so.ApplyModifiedProperties();
        return true;
    }


    private bool ComponentTypeExcluded(Type t)
    {
        for (var i = 0; i < ignoreType.Length; i++)
        {
            var iType = ignoreType[i];
            if (t == iType.type || (iType.inCludeSubType && t.IsSubclassOf(iType.type))) return true;
        }
        return false;
    }

    private bool GameObjectNameIncluded(string name)
    {
        return name.Contains("_");
    }

    public Type GetSpecifiedClass(string name)
    {
        foreach (var kv in classNameDic)
        {
            if (name.EndsWith(kv.Key.Name)) return kv.Key;
            for (var i = 0; i < kv.Value.Length; i++)
            {
                if (name.EndsWith(kv.Value[i]))
                {
                    return kv.Key;    
                }
            }
        }

        return null;
    }

    private void RemoveGameObjectNameIncluded(ref string varName)
    {
        if (varName.StartsWith("_")) varName = varName.Substring(1);
        varName = varName.Replace(" ", "");
    }

    //名字满足条件的GameObject和子物体全部跳过
    private bool GameObjectNameSkipped(string name)
    {
        return name.StartsWith("$");
    }

    private void ClearScriptBinderInChildren(ScriptBinder sb)
    {
        var sbArr = sb.GetComponentsInChildren<ScriptBinder>(true);
        for (var i = 0; i < sbArr.Length; i++)
        {
            var childSB = sbArr[i];
            var so = new SerializedObject(childSB);
            so.FindProperty("scriptItems").arraySize = 0;
            so.ApplyModifiedProperties();
            UnityEngine.Debug.LogErrorFormat("<color=#ff0000>clear ScriptBinder name = [<color=#ffff00>{0}</color>]!</color>", childSB.name);
        }
    }

    class IgnoreType
    {
        public bool inCludeSubType;
        public Type type;

        public IgnoreType(Type type, bool inCludeSubType = false)
        {
            this.type = type;
            this.inCludeSubType = inCludeSubType;
        }
    }
}
#endif